import { Injectable } from "@angular/core";
import {
  Http,
  RequestOptions,
  Headers,
  Response,
  QueryEncoder,
} from "@angular/http";
import { Observable } from "rxjs/Rx";
import { DevExtremeModule, DxDataGridComponent } from "devextreme-angular";
import CustomStore from "devextreme/data/custom_store";
import CustomStoreOptions from "devextreme/data/custom_store";
import "rxjs/add/operator/map";
import "rxjs/add/operator/catch";
import notify from "devextreme/ui/notify";
import { ParamUtil } from "./paramUtil";
import ENV from "../../tools/config";

/**
 * 工具
 */
@Injectable()
export class RestUtil {
  //静态变量, 错误提示消息的类型. 两值: 'error','warning'
  static errorTipsType: string = "error";
  constructor(private http: Http) {}

  //=====================================
  // 实例方法区
  //==================================== 
  /**
   * 加载常规数据
   * @param url request address
   * @param args input parameter
   */
  loadDics(url: string, args?: any): Observable<any> {
    let callback = Callback.dics;
    return this.__postMap(this.http, url, callback, null);
  }

  /**
   * 加载常规数据
   * @param url request address
   * @param args input parameter
   */
  loadData(url: string, args?: any): Observable<any> {
    let callback = Callback.form;
    let params = ParamUtil.wrapForm(args);
    return this.__postMap(this.http, url, callback, params);
  }
  /**
   * 加载列表数据
   * @param url request address
   * @param args input parameter
   */
  loadItems(url: string, args?: any): Observable<any> {
    let callback = Callback.items;
    let params = ParamUtil.wrapForm(args);
    return this.__postMap(this.http, url, callback, params);
  }
  /**
   * 加载树形数据
   * @param url
   * @param loadOptions
   * @param condi
   */
  loadTree(url: string, condi: object = {}): Promise<any> {
    let callback = Callback.treeSearch;
    let params = ParamUtil.wrapForm(condi);
    return this.__gridPromise(this.http, [url], params, callback);
  }
  /**
   * 常规表单请求
   * @param url
   * @param args
   * @param wrapForm
   */
  post(
    url: string,
    args: any = null,
    wrapForm: boolean = true
  ): Observable<any> {
    // 是否序列化参数
    if (wrapForm && args != null) {
      let params = ParamUtil.wrapForm(args);
      console.log('params = ',params);
      return this.__postForm(this.http, url, params);
    } else {
      return this.__postForm(this.http, url, args);
    }
  }
  /**
   * 常规表单请求
   * @param url
   * @param args
   * @param wrapForm
   */
  postPromise(
    url: string,
    args: any = null,
    wrapForm: boolean = true,
    callbackFunc?: any
  ): Promise<any> {
    // 是否序列化参数
    if (wrapForm && args != null) {
      let params = ParamUtil.wrapForm(args);
      // console.log('params = ',params);
      return this.__post(this.http, [url], params, callbackFunc);
    } else {
      return this.__post(this.http, [url], args, callbackFunc);
    }
  }
  /**
   * 删除操作
   * @param url
   * @param args
   */
  del(url: string, args?: {}): Observable<any> {
    return this.post(url, args, false);
  }
  /**
   * 更新操作
   * @param url
   * @param args
   */
  update(url: string, args?: {}): Observable<any> {
    return this.post(url, args, false);
  }

  /**
   * 动态表格请求
   * @param url
   * @param args
   * @param callback
   */
  grid(url: string, args?: string, callback?: any): Promise<any> {
    return this.__gridPromise(this.http, [url], args, callback);
  }

  /**
   * 动态表格搜索
   * @param url
   * @param loadOptions
   * @param condi
   */
  search(
    url: string,
    loadOptions: CustomStoreOptions = null,
    condi: object = {}
  ): Promise<any> {
    let params = ParamUtil.wrapParams(condi, loadOptions);
    return this.__gridPromise(this.http, [url], params);
  }

  /**
   * 入参: grid格式
   * 出参: items
   */
  items(url: string, args?: any): Promise<any> {
    let callback = Callback.items;
    let params = ParamUtil.wrapParams(args);
    // console.log('params = ',args);

    return this.__gridPromise(this.http, [url], params, callback);
  }

  /**
   * 文件上传
   * @param url
   * @param args
   */
  uploadFile(url: string, args?: any): Observable<any> {
    // 统一处理参数
    let mFormParam = ParamUtil.wrapMultiForm(args);
    console.log("mFormParam = ", mFormParam);
    return this.__postFile(this.http, url, mFormParam);
  }

  /**
   * 下载
   */
  downloadFile(url: string, params?: any) {
    var iframe = document.createElement("iframe"),
      //将下载的文件进行转换类型
      obj2String = function (obj) {
        var str = "";
        if (obj != null) {
          for (var key in obj) {
            str += key + "=" + obj[key] + "&";
          }
        }
        str = str.substring(0, str.length - 1);
        //加密传输开关打开
        if (ENV.ipMap.isEncrypt == true) {
          str = ENV.encode2(str);
        }
        return str;
      };
    iframe.src = `${url}?${obj2String(params)}`;
    document.body.appendChild(iframe);
    iframe.style.display = "none";
  }

  // =====================================
  // 私有方法区
  // =====================================
  // static queryEncoder: QueryEncoder = new QueryEncoder();
  /**
   * 加载单条数据
   * @param http
   * @param url 请求地址
   * @param body 入参
   */
  private __postForm(http: Http, url: string, body?: any): Observable<any> {
    let callback = Callback.form;
    return this.__postMap(http, url, callback, body);
  }

  /**
   * post请求 Map处理结果集
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param http
   * @param url 请求地址
   * @param callBack 回调函数
   * @param body 请求参数
   * @param options post请求标头与格式
   */
  private __postMap(
    http: Http,
    url: string,
    callBack: any,
    body?: any,
    options?: RequestOptions
  ): Observable<any> {
    if (options == undefined) {
      let headers = new Headers({
        dataType: "json",
        "Content-Type": "application/json",
      });
      let t: string = ParamUtil.getToken();
      t != null && headers.append("token", t);
      options = new RequestOptions({ headers: headers });
    }
    // console.log('body',body);

    // 设置token
    let isNotNull: boolean = body != null;
    let token: any = ParamUtil.getToken();
    //非空判断
    if (token != null) {
      // 非空 && 是字符串
      if (isNotNull && typeof body == "string") {
        try {
          let json: any = JSON.parse(`${body}`);
          json["token"] = ParamUtil.getToken();
          body = JSON.stringify(json);
        } catch (e) {
          // 是另一种形式:
          // 如: items="{'a':'b'}"
          // token = RestUtil.queryEncoder.encodeValue(token);
          body += `&token=${token}`;
        }
      }
      // 非空 && 是对象
      else if (isNotNull && typeof body == "object") {
        token = { token: token };
        // 是数组
        if (body instanceof Array) {
          body = Object.assign(
            {},
            {
              data: body,
            }
          );
        }
        Object.assign(body, token);
        body = JSON.stringify(body);
      }
      // 其他情况
      else {
        let json = { token: token };
        body = JSON.stringify(json);
      }
    } // token is not null end
    // if (typeof body == 'string'
    //     && body.split("%22").length == 1
    //     && body.indexOf("{") > -1
    //     && body.indexOf("params=") > -1
    // )
    // {
    //     let beginIndex = body.indexOf("=");
    //     let content = body.substring(beginIndex + 1,body.length);
    //     body ="params="+ encodeURIComponent(content);
    // }
    //url
    url.indexOf("?") == -1 && (url += `?rn=${new Date().getTime()}`);
    return http
      .post(url, body, options)
      .map(callBack)
      .catch((error) => {
        return RestUtil.throwError(error.message);
      });
  }

  /**
   * post请求 导入文件
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param http
   * @param url 请求地址
   * @param body 入参 格式 : [...File,{}]
   */
  private __postFile(http: Http, url: string, body?: any): Observable<any[]> {
    // FormData的用法 :
    //  append: 当append一个非文件类型的值时,第一次的赋值起作用;之后的所有值都不起作用;
    //          当多次append一个文件类型的值时,会产生一个数组值 ;
    //  set:    当set一个非文件类型的值时,以最后一次set的值为准;
    //          当多次set一个任何类型的值时,不会产生数组;
    //
    //
    // @date 20200523
    // @author niushengsheng
    // formData.has() 方法有兼容问题。
    // 解决方案如下：
    // let fm = new FormData();
    // if(fm.get("form") == null){
    //     fm.set("form","{}");
    // }

    let form = new FormData();
    // console.log("body",body);

    body &&
      body.map &&
      body.map((a, idx, self) => {
        if (a instanceof File) {
          form.append("file", a);
        }
        //文件对象
        else if (a.hasOwnProperty("file")) {
          form.append("file", a["file"]);
        }
        //非文件对象
        else {
          console.log("非文件类型", a);
          var tmp = "";
          form.append("form", JSON.stringify(a));
        }
        return a;
      });
    if (form.get("form") == null) {
      form.append("form", "{}");
    }
    //设置token
    if (form.get("token") == null) {
      form.append("token", ParamUtil.getToken());
    }

    // let tmp : any = {
    //     name : 'tim',
    //     pwd : 'cook'
    // }
    // form.append('form',JSON.stringify(tmp));
    // console.log("form",form.get('file'));

    return http
      .post(url, form)
      .map(Callback.form)
      .catch((error: any) => {
        return RestUtil.throwError(error.message);
      });
  }

  /**
   * post请求 Promise处理结果集
   * @Time 2017-12-26
   * @author niu shengsheng
   * @param url 请求地址
   */

  private __gridPromise(
    http: Http,
    url: string[],
    params?: any,
    callbackFunc?: any
  ): any {
    // 回调函数
    let func = Callback.grid;
    // 请求地址
    let tmpUrl = url.join("/");

    //若用户传了回调函数, 就使用入参
    if (callbackFunc != null && typeof callbackFunc == "function") {
      func = callbackFunc;
    }
    // 返回
    return this.__postPromise(http, tmpUrl, func, params);
  }

  /**
   * post请求 Promise处理结果集
   * @Time 2017-12-26
   * @author niu shengsheng
   * @param url 请求地址
   */

  private __post(
    http: Http,
    url: Array<string>,
    params?: any,
    callbackFunc?: any
  ): any {
    // 回调函数
    let func = Callback.form;
    // 请求地址
    let tmpUrl = url.join("/");

    //若用户传了回调函数, 就使用入参
    if (callbackFunc != null && typeof callbackFunc == "function") {
      func = callbackFunc;
    }
    // 返回
    return this.__postPromise(http, tmpUrl, func, params);
  }

  /**
   * post请求 Promise处理结果集
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param http
   * @param url 请求地址
   * @param callBack 回调函数
   * @param options post请求标头与格式
   */
  private __postPromise(
    http: Http,
    url: string,
    callBack: any,
    body: any,
    options?: RequestOptions
  ): any {
    if (options == undefined) {
      let headers = new Headers({
        dataType: "json",
        "Content-Type": "application/json",
      });
      // TOKEN参数的拼装
      let t: string = ParamUtil.getToken();
      t != null && headers.append("token", t);
      options = new RequestOptions({ headers: headers });
    }

    url.indexOf("?") == -1 && (url += `?rn=${new Date().getTime()}`);
    return http
      .post(url, body, options)
      .toPromise()
      .then(callBack)
      .catch((error) => {
        return RestUtil.rejectError(error.message);
      });
  }

  /**
   * 统一异常处理
   * @param error 字符串类型
   */
  static handleError(error: string): any {
    console.log("handleError", error);

    var msg = "";
    var idx = -1,
      isNum = -1;
    var isChinese = false;
    var isDataComplete = false;

    if (error == null) {
      msg = ErrorCode.SYS_000;
      notify(
        {
          message: msg,
          position: {
            my: "center top",
            at: "center top",
          },
        },
        RestUtil.errorTipsType,
        3000
      );
      // notify(msg);
      // 重置错误消息类型
      RestUtil.resetErrorTipsType();
      return Observable.throw(error);
    }

    // 请求异常处理
    error = error.trim();
    idx = error.search(/^SYS_[0-9]{3}$/);
    isNum = error.search(/^[0-9]{3}$/);
    isChinese = error.charCodeAt(0) > 128;
    isDataComplete =
      error.startsWith("PES_900") ||
      error.startsWith("PES_901") ||
      error.startsWith("PES_902") ||
      error.startsWith("PES_903");

    // 默认异常提醒
    if (error == "") {
      msg = ErrorCode.SYS_001;
    }
    // 中文描述
    else if (isChinese) {
      msg = error;
    }
    // 无法识别的
    else if (idx == -1 && isNum == -1) {
      msg = ErrorCode.SYS_000;
    }
    // 是数字
    else if (isNum > -1) {
      msg = ErrorCode[`SYS_${error}`] || ErrorCode["SYS_000"];
    }
    // 能正常识别的
    else if (idx > -1) {
      msg = ErrorCode[error] || ErrorCode["SYS_000"];
    }
    if (isDataComplete) {
      msg = ErrorCode["PES_900"];
    }
    notify(
      {
        message: msg,
        position: {
          my: "center top",
          at: "center top",
        },
      },
      RestUtil.errorTipsType,
      3000
    );
    // notify(msg);
    // return Observable.throw(error);
    // 重置错误消息类型
    RestUtil.resetErrorTipsType();
    return error;
  }
  /**
   * Observable方式的异常处理
   *
   */
  static throwError(error: string): any {
    error = RestUtil.handleError(error);
    return Observable.throw(error);
  }
  /**
   * Promise方式异常处理
   */
  static rejectError(error: string) {
    error = RestUtil.handleError(error);
    return Promise.reject(error);
  }

  /**
   * 重置错误消息类型
   */
  static resetErrorTipsType() {
    RestUtil.errorTipsType = "error";
  }

  /**
   * 警告错误消息类型
   */
  static setWarning2ErrorTipsType() {
    RestUtil.errorTipsType = "warning";
  }
}

/**
 * 回调函数
 * @author niushengsheng
 * @date 20200804
 */
export const Callback = {

  /**
   * 字典callback
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param response 回调结果
   * @return 保存是否成功
   */
  dics(response: any): boolean {
    return response.json();
  },

  /**
   * 保存callback
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param response 回调结果
   * @return 保存是否成功
   */
  save(response: any): boolean {
    //校验response是否符合规则
    return Callback.def(response);
  },

  /**
   * 表单callback
   * @Time 2018-03-30
   * @author niushengsheng
   * @param response 回调结果
   * @return 保存是否成功
   */
  form(response: any): any {
    // console.log('callbackForm ',response);
    //校验response是否符合规则
    return Callback.def(response);
  },

  /**
   * 保存callback
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param response 回调结果
   * @return 保存是否成功
   */
  def(response: any): any {
    //校验response是否符合规则
    let repJson: any = Callback.valid(response);
    return repJson.resultValue;
  },

  /**
   * 保存callback
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param response 回调结果
   * @return 保存是否成功
   */
  items(response: any): any {
    let repJson = Callback.def(response);

    if (repJson && repJson.items) {
      return repJson.items;
    }
    return repJson;
  },

  /**
   * 查询个数 resultValue 直接是需要的值
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param response 回调结果
   * @return 查询结果JSON对象
   */
  count(response: any): any {
    let repJson = Callback.def(response);

    return {
      data: repJson,
    };
  },

  /**
   * 保存callback
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param response 回调结果
   * @return 保存是否成功
   */
  full(response: any): any {
    //校验response是否符合规则
    let repJson: any = Callback.valid(response);
    return {
      body: repJson,
      headers: response.headers,
    };
  },
  /**
   * post请求 Promise处理结果集
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param http
   * @param url 请求地址
   * @param callBack 回调函数
   * @param options post请求标头与格式
   */
  valid(resp: any): Object {
    let repJson = null;
    let error = new Error();
    try {
      repJson = resp.json();

      //响应格式为NULL
      if (repJson == null) {
        throw error;
      }

      if (repJson.successful == undefined) {
        console.log("response.successful is undefined");
        throw error;
      }
      if (!repJson.successful) {
        // console.log("response.successful is false");
        error.message = repJson.resultHint;
        throw error;
      }
    } catch (e) {
      throw e;
    }
    return repJson;
  },
  
  /**
   * dataGrid查询callback
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param response 回调结果
   * @return 查询结果JSON对象
   */
  grid(response: any): any {
    // 应对CustomStore的接收数据格式
    // 默认返回固定格式
    let ret = {
      data: [],
      totalCount: 0,
    };
    //校验response是否符合规则
    let repJson: any = Callback.valid(response);

    if (repJson.resultValue != null) {
      let obj = {
        data: repJson.resultValue.items,
        totalCount: repJson.resultValue.itemCount,
      };
      return obj;
    }
    return ret;
  },
  /**
   * 树形callback
   * @Time 2017-12-26
   * @author Zhong Yang
   * @param response 回调结果
   * @return 查询结果JSON对象
   */
  treeSearch(response: any): any {
    // 应对CustomStore的接收数据格式
    // 默认返回固定格式
    let obj = [];
    //校验response是否符合规则
    let repJson: any = Callback.valid(response);

    if (repJson.resultValue != null) {
      // console.log("打印数据长度")
      // console.log(repJson.resultValue)
      for (let i = 0; i < repJson.resultValue.length; i++) {
        let item = repJson.resultValue[i];
        let item1 = {
          id:       parseInt(item["id"]),
          parentId: parseInt(item["parentId"]),
          text:     item["text"],
          isGroup:  item["isGroup"],
          code:     item["code"],
        };
        if (item["items"] && item["items"].length > 0)
          item1["items"] = Callback.convert(item["items"]);
        obj.push(item1);
      }
    }
    return obj;
  },
  convert(objArr: Array<object>) {
    console.log("convert");
    let resultArr = null;
    if (objArr && objArr.length > 0) {
      resultArr = new Array<object>();
      for (let i = 0; i < objArr.length; i++) {
        let item = objArr[i];
        let item1 = {
          id:       parseInt(item["id"]),
          parentId: parseInt(item["parentId"]),
          text:     item["text"],
          isGroup:  item["isGroup"],
          code:     item["code"],
        };
        if (item["items"] && item["items"].length > 0 && i < 2000){
          item1["items"] = Callback.convert(item["items"]);
        }
        resultArr.push(item1);
      }
    }
    return resultArr;
  }

}

/**
 * 错误代码
 */
export const ErrorCode = {
  SUCCESS: "响应成功",
  SYS_000: "服务器异常",
  SYS_001: "服务器异常",
  SYS_003: "入参必选参数非空",
  SYS_004: "从DB中加载对象失败",
  SYS_005: "导入表头不一致",
  SYS_006: "导入无数据",
  SYS_007: "没找到任务项和物资中类关联记录",
  SYS_008: "有多条物资中类和此任务项关联",
  SYS_009: "没找到记录",
  SYS_010: "没有编制单位",
  SYS_011: "没有审核单位",
  SYS_012: "没有物资中类关联",
  SYS_013: "没有对应的采购标准编码",
  SYS_014: "第二个sheet页没有数据",
  SYS_015: "第三个个sheet页没有数据",
  SYS_016: "第二个sheet页表头错误，请重新下载模板",
  SYS_017: "第三个sheet页表头错误，请重新下载模板",
  SYS_018: "任务状态已结束",
  SYS_019: "导入的excel为空，请重新导入",
  SYS_020: "任务类别错误，请重新编写后导入",
  SYS_021: "表头个数错误，请重新下载后导入",
  SYS_022: "表头内容错误，请重新下载后导入",
  SYS_023: "模板与物资关系不存在",
  SYS_024: "第一个sheet页没有数据",
  SYS_025: "第一个sheet应该只有一行数据",
  SYS_026: "第一个sheet页表头错误，请重新下载模板",
  SYS_027: "通用采购标准编码输入有误",
  SYS_028: "物资分类编码输入有误",
  SYS_029: "创建任务失败",
  //	开始时间不合法
  SYS_030: "开始时间不合法",
  //	结束时间不合法
  SYS_031: "结束时间不合法",
  //	任务名称不能为空
  SYS_032: "任务名称不能为空",
  //	任务类型不能为空
  SYS_033: "任务类型不能为空",
  //	未找到技术规范项数据
  SYS_050: "未找到技术规范项数据",
  //  验证失败
  SYS_051: "验证失败",
  PES_900: "数据完整性被破坏,请联系管理员",
};
